﻿using DotNetCommon.Data;
using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace DBUtil.Builders;

public class DeleteBuilder : WhereBuilder
{
    protected static readonly ILogger<DeleteBuilder> logger = LoggerFactory.CreateLogger<DeleteBuilder>();
    public EnumDeleteBuilderType Type { get; internal set; }
    public TableName TableName { get; internal set; }
    /// <summary>
    /// 主键值
    /// </summary>
    public object Object { get; set; }
    public DeleteBuilder(DBAccess db, string tableName, object obj) : base(db)
    {
        Type = EnumDeleteBuilderType.Delete;
        Object = obj;
        TableName = db.ParseQuotedName(tableName);
    }

    #region AsTable
    public virtual DeleteBuilder AsTable(string tableName)
    {
        TableName = db.ParseQuotedName(tableName);
        return this;
    }
    public virtual DeleteBuilder AsTableIf(bool condition, string tableName)
        => condition ? AsTable(tableName) : this;
    public virtual DeleteBuilder AsTable(Func<string, string> func)
    {
        var newTablePureName = func?.Invoke(TableName.Name);
        if (newTablePureName.IsNotNullOrWhiteSpace()) TableName.SetName(newTablePureName);
        return this;
    }
    public virtual DeleteBuilder AsTableIf(bool condition, Func<string, string> func)
        => condition ? AsTable(func) : this;
    public virtual DeleteBuilder AsTable(Action<TableName> func)
    {
        func?.Invoke(TableName);
        return this;
    }
    public virtual DeleteBuilder AsTableIf(bool condition, Action<TableName> func)
        => condition ? AsTable(func) : this;
    #endregion
    #region 复写 WhereSeg
    public override DeleteBuilder WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder;
    public override DeleteBuilder WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder;
    public override DeleteBuilder WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder;
    public override DeleteBuilder WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder;
    public override DeleteBuilder WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder;
    public override DeleteBuilder WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder;
    public override DeleteBuilder WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder;
    public override DeleteBuilder WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder;
    #endregion
    #region  复写 Where
    public override DeleteBuilder Where(string filter)
        => base.Where(filter) as DeleteBuilder;
    public override DeleteBuilder WhereIf(bool condition, string filter)
        => base.WhereIf(condition, filter) as DeleteBuilder;
    #endregion
    #region 复写 CommandTimeout
    public override DeleteBuilder CommandTimeout(int timeoutSeconds)
        => base.CommandTimeout(timeoutSeconds) as DeleteBuilder;
    public override DeleteBuilder CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion

    #region ToSql ExecuteAffrows(Async)
    /// <summary>
    /// 返回生成的sql
    /// </summary>
    public virtual string ToSql()
    {
        //先校验
        if (Type == EnumDeleteBuilderType.Delete && Filters.IsNullOrEmpty()) throw new Exception("不允许执行无 where 条件的delete语句!");
        var sb = new StringBuilder();
        sb.Append("delete from ").Append(TableName.FullNameQuoted);
        //处理WhereSeg Where
        if (Filters.IsNotNullOrEmpty())
        {
            var filterSql = this.DealFilter(Filters);
            if (filterSql.IsNotNullOrEmpty()) sb.Append(" where ").Append(filterSql);
        }
        sb.Append(';');
        var sql = sb.ToString();
        sb.Clear();
        return sql;
    }

    public virtual int ExecuteAffrows()
    {
        var sql = ToSql();
        return RunWriteMonitor(new AfterWriteArgument
        {
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.Delete
        }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
    }
    public virtual async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
    {
        var sql = ToSql();
        return await RunWriteMonitorAsync(new AfterWriteArgument
        {
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.Delete
        }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
    }
    #endregion
}

/// <summary>
/// 删除构造器
/// </summary>
public class DeleteBuilder<T> : DeleteBuilder where T : class, new()
{
    protected static new readonly ILogger<DeleteBuilder<T>> logger = LoggerFactory.CreateLogger<DeleteBuilder<T>>();
    private EntityInfo EntityInfo { get; set; }
    #region 初始化
    public DeleteBuilder(DBAccess db, EnumDeleteBuilderType type, object obj) : base(db, db.GetEntityInfoInternal<T>().TableName.FullNameQuoted, obj)
    {
        this.Type = type;
        this.EntityInfo = db.GetEntityInfoInternal<T>(false);
        if (type == EnumDeleteBuilderType.DeleteByPrimary && obj == null) throw new Exception("必须设置主键值!");
    }
    #endregion
    #region 复写 AsTable
    public override DeleteBuilder<T> AsTable(string tableName)
    {
        base.AsTable(tableName);
        return this;
    }
    public override DeleteBuilder<T> AsTableIf(bool condition, string tableName)
        => condition ? AsTable(tableName) : this;
    public override DeleteBuilder<T> AsTable(Func<string, string> func)
        => base.AsTable(func) as DeleteBuilder<T>;
    public override DeleteBuilder<T> AsTableIf(bool condition, Func<string, string> func)
        => condition ? AsTable(func) : this;
    public override DeleteBuilder AsTable(Action<TableName> func)
        => base.AsTable(func) as DeleteBuilder<T>;
    public override DeleteBuilder AsTableIf(bool condition, Action<TableName> func)
        => base.AsTable(func) as DeleteBuilder<T>;
    #endregion
    #region 复写 WhereSeg
    public override DeleteBuilder<T> WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        => base.WhereSegIf(condition, filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        => base.WhereSeg(filter) as DeleteBuilder<T>;
    #endregion
    #region  复写 Where
    public override DeleteBuilder<T> Where(string filter)
        => base.Where(filter) as DeleteBuilder<T>;
    public override DeleteBuilder<T> WhereIf(bool condition, string filter)
        => base.WhereIf(condition, filter) as DeleteBuilder<T>;
    #endregion
    #region 复写 CommandTimeout
    public override DeleteBuilder<T> CommandTimeout(int timeoutSeconds)
        => base.CommandTimeout(timeoutSeconds) as DeleteBuilder<T>;
    public override DeleteBuilder<T> CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion
    #region Where
    public DeleteBuilder<T> Where(Expression<Func<T, bool>> filter)
    {
        Filters.Add(filter);
        return this;
    }
    public DeleteBuilder<T> WhereIf(bool condition, Expression<Func<T, bool>> filter)
    {
        if (!condition) return this;
        return Where(filter);
    }
    #endregion

    #region ToSql ExecuteAffrows(Async)
    public override string ToSql()
    {
        //先校验
        if (Type == EnumDeleteBuilderType.Delete) return base.ToSql();
        var sb = new StringBuilder();
        sb.Append("delete from ").Append(TableName.FullNameQuoted);
        var hasWhere = false;

        //先处理按主键删除
        if (Type == EnumDeleteBuilderType.DeleteByPrimary)
        {
            var primaryMulti = EntityInfo.Props.Where(i => i.IsPrimaryKey).OrderBy(i => i.Order).ToList();
            var primary = EntityInfo.PrimaryKeyColumn;
            if (primary == null)
                throw new Exception($"必须声明主键列({EntityInfo.TypeClassFullName}),否则无法使用 DeleteByPrimary！");

            var seg = db.ConvertToSqlSeg(Object).UnWrap();
            var cols = primary.ColumnNameQuoted;
            if (primaryMulti.Count > 1) cols = "(" + primaryMulti.Select(i => i.ColumnNameQuoted).ToStringSeparated(",") + ")";
            sb.Append(" where ").Append(cols).Append(Object is IEnumerable ? " in " : " = ").Append(seg);
            hasWhere = true;
        }
        //处理WhereSeg Where
        if (Filters.IsNotNullOrEmpty())
        {
            var filterSql = this.DealFilter(Filters);
            if (filterSql.IsNotNullOrEmpty())
            {
                if (hasWhere) sb.Append(" and ").Append('(').Append(filterSql).Append(')');
                else sb.Append(" where ").Append(filterSql);
            }
        }
        sb.Append(';');
        var sql = sb.ToString();
        sb.Clear();
        return sql;
    }

    public override int ExecuteAffrows()
    {
        var sql = ToSql();
        return RunWriteMonitor(new AfterWriteArgument
        {
            EntityInfo = EntityInfo,
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.Delete
        }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
    }
    public override async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
    {
        var sql = ToSql();
        return await RunWriteMonitorAsync(new AfterWriteArgument
        {
            EntityInfo = EntityInfo,
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.Delete
        }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
    }
    #endregion
}